﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Caching;
using System.Collections.Specialized;

namespace Shop.Infrastructure.Caching
{
    /// <summary>
    /// Provides a set of cache features and exposes a set of methods to the default cache provider.
    /// </summary>
    public static class CacheManager
    {
        static bool isInit = false;
        static CacheProvider provider;
        static CacheProviderCollection providers;
        static Exception initEx;
        static object syncRoot = new object();

        static void Init()
        {
            if (!isInit)
            {
                lock (syncRoot)
                {
                    if (!isInit)
                    {
                        try
                        {
                            provider = new MemoryCacheProvider();
                            provider.Initialize("TempMemoryCacheProvider", new NameValueCollection());
                            CacheProviderCollection providerColl = new CacheProviderCollection();
                            providerColl.Add(provider);
                            providers = providerColl;
                        }
                        catch (Exception ex)
                        {
                            initEx = ex;
                            throw;
                        }
                        isInit = true;
                    }
                }
            }
            else if (initEx != null)
            {
                throw initEx;
            }
        }

        /// <summary>
        /// Gets the default cache provider.
        /// </summary>
        public static CacheProvider Provider
        {
            get
            {
                Init();
                return provider;
            }
        }

        public static CacheProviderCollection Providers
        {
            get
            {
                Init();
                return providers;
            }
        }

        public static bool Add(CacheItem item, CacheItemPolicy policy)
        {
            return Provider.Add(item, policy);
        }

        public static bool Add(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
        {
            return Provider.Add(key, value, absoluteExpiration, regionName);
        }

        public static bool Add(string key, object value, TimeSpan slidingExpiration, string regionName = null)
        {
            return Provider.Add(key, value, slidingExpiration, regionName);
        }

        /// <summary>
        /// Tries to add the specified entry to the default cache provider.
        /// </summary>    
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="policy"></param>
        /// <param name="regionName"></param>
        /// <returns>True if add succeeded, or false if there is an already an entry in the cache that has the same key as item.</returns>
        public static bool Add(string key, object value, CacheItemPolicy policy, string regionName = null)
        {
            return Provider.Add(key, value, policy, regionName);
        }

        public static bool Contains(string key, string regionName = null)
        {
            return Provider.Contains(key, regionName);
        }

        public static void Clear(string regionName = null)
        {
            Provider.Clear(regionName);
        }

        public static CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<string> keys, string regionName = null)
        {
            return Provider.CreateCacheEntryChangeMonitor(keys, regionName);
        }

        public static DefaultCacheCapabilities DefaultCacheCapabilities
        {
            get
            {
                return Provider.DefaultCacheCapabilities;
            }
        }

        public static object Get(string key, string regionName = null)
        {
            return Provider.Get(key, regionName);
        }

        /// <summary>
        /// Gets the cached value from the default provider. If not found, returns the default value of <typeparamref name="T"/>.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="regionName"></param>
        /// <returns></returns>
        public static T Get<T>(string key, string regionName = null)
        {
            return Provider.Get<T>(key, regionName);
        }

        /// <summary>
        /// Tries to get the cached value from the default provider. If not found, returns the specified <paramref name="defaultValue"/>.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="defaultValue"></param>
        /// <param name="regionName"></param>
        /// <returns></returns>
        public static T Get<T>(string key, T defaultValue, string regionName = null)
        {
            return Provider.Get<T>(key, regionName);
        }

        public static CacheItem GetCacheItem(string key, string regionName = null)
        {
            return Provider.GetCacheItem(key, regionName);
        }

        public static IDictionary<string, object> GetValues(IEnumerable<string> keys, string regionName = null)
        {
            return Provider.GetValues(keys, regionName);
        }

        public static object Remove(string key, string regionName = null)
        {
            return Provider.Remove(key, regionName);
        }

        /// <summary>
        /// Inserts a item to the default cache provider.
        /// </summary>
        /// <remarks>
        /// If an item that matches key does not exist in the cache, value and key are used to insert as a new cache entry. 
        /// If an item with a key that matches item exists, the cache entry is updated or overwritten by using value.
        /// </remarks>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="policy"></param>
        /// <param name="regionName"></param>
        public static void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
        {
            Provider.Set(key, value, policy, regionName);
        }

        public static void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
        {
            Provider.Set(key, value, absoluteExpiration, regionName);
        }

        public static void Set(string key, object value, TimeSpan slidingExpiration, string regionName = null)
        {
            Provider.Set(key, value, slidingExpiration, regionName);
        }

        public static CacheProvider MemcachedProvider
        {
            get
            {
                return CacheManager.Providers["Memcached"] ?? CacheManager.Provider;
            }
        }
    }
}
